package utils.fileConvert;

import com.alibaba.fastjson.*;
import com.google.common.collect.Lists;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.converter.BidirectionalConverter;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.ClassMapBuilder;
import ma.glasnost.orika.metadata.Type;
import org.apache.commons.collections4.CollectionUtils;

import java.time.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 对象类型转换
 * BeanMapperUtils
 *
 * @Date 14:20 2019/8/16
 **/
public class BeanMapperUtils {

	private static AtomicBoolean f = new AtomicBoolean(false);

	private static MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
	private static MapperFactory noNullmapperFactory = new DefaultMapperFactory.Builder()
			.mapNulls(false).build();

	public static void map(Object source, Object target, boolean mapNullProperty) {
		if (null == source || null == target) {
			return;
		}
		MapperFactory tempFactory = getMapperFactory(mapNullProperty);
		tempFactory.classMap(source.getClass(), target.getClass());
		tempFactory.getMapperFacade().map(source, target);
	}

	private static MapperFactory getMapperFactory(boolean mapNullProperty) {
		MapperFactory tempFactory = mapperFactory;
		if (!mapNullProperty) {
			tempFactory = noNullmapperFactory;
		}
		if (!f.get()) {
			synchronized (BeanMapperUtils.class) {
				if (f.compareAndSet(false, true)) {
					tempFactory.getConverterFactory().registerConverter(new LocalDateConverter());
					tempFactory.getConverterFactory()
							.registerConverter(new LocalDateTimeConverter());
					tempFactory.getConverterFactory().registerConverter(new LocalTimeConverter());
					tempFactory.getConverterFactory()
							.registerConverter(new DateLocalDateConverter());
					tempFactory.getConverterFactory()
							.registerConverter(new DateLocalDateTimeConverter());
					tempFactory.getConverterFactory()
							.registerConverter(new DateLocalTimeConverter());
					tempFactory.getConverterFactory()
							.registerConverter(new JSONConverter());
					tempFactory.getConverterFactory()
							.registerConverter(new JSONArrayConverter());
					tempFactory.getConverterFactory()
							.registerConverter(new JSONObjectConverter());
				}
			}
		}
		return tempFactory;
	}


	public static <T> T map(Object source, Class<T> destinationClass, List<CoupleField> pairs,
	                        List<String> excludes, boolean mapNullProperty) {
		if (source == null) {
			return null;
		}
		MapperFactory tempFactory = getMapperFactory(mapNullProperty);
		ClassMapBuilder cmb = tempFactory.classMap(source.getClass(), destinationClass);
		if (CollectionUtils.isNotEmpty(pairs)) {
			pairs.forEach(t -> cmb.field(t.getSt1(), t.getSt2()));
		}
		if (CollectionUtils.isNotEmpty(excludes)) {
			excludes.forEach(t -> cmb.exclude(t));
		}
		cmb.byDefault().register();
		return tempFactory.getMapperFacade().map(source, destinationClass);
	}

	public static <T> T map(Object source, Class<T> destinationClass) {
		return source == null ? null : getMapperFactory(true).getMapperFacade()
				.map(source, destinationClass);
	}


	public static <T> List<T> mapList(Collection sourceList, Class<T> destinationClass) {
		if (sourceList == null) {
			return null;
		} else {
			List<T> destinationList = Lists.newArrayList();
			Iterator var3 = sourceList.iterator();

			while (var3.hasNext()) {
				Object sourceObject = var3.next();
				T destinationObject = getMapperFactory(true).getMapperFacade()
						.map(sourceObject, destinationClass);
				destinationList.add(destinationObject);
			}

			return destinationList;
		}
	}

	/**
	 * @param source|源对象
	 * @param target|目标对象
	 * @param mapNullProperty|是否允许拷贝空属性
	 * @param excludes|指定需要排除的属性
	 */
	public static void map(Object source, Object target, boolean mapNullProperty, List<String> excludes) {
		if (null == source || null == target) {
			return;
		}
		MapperFactory tempFactory = getMapperFactory(mapNullProperty);
		ClassMapBuilder<?, ?> classMapBuilder = tempFactory.classMap(source.getClass(), target.getClass());
		if (CollectionUtils.isNotEmpty(excludes)) {
			excludes.forEach(classMapBuilder::exclude);
		}
		classMapBuilder.byDefault().register();
		tempFactory.getMapperFacade().map(source, target);
	}

	public static class CoupleField {
		private String st1;
		private String st2;

		public CoupleField(String st1, String st2) {
			this.st1 = st1;
			this.st2 = st2;
		}

		public String getSt1() {
			return st1;
		}

		public void setSt1(String st1) {
			this.st1 = st1;
		}

		public String getSt2() {
			return st2;
		}

		public void setSt2(String st2) {
			this.st2 = st2;
		}
	}


	public static class LocalDateConverter extends BidirectionalConverter<LocalDate, LocalDate> {

		@Override
		public LocalDate convertTo(LocalDate source, Type<LocalDate> destinationType) {
			return LocalDate.from(source);
		}

		@Override
		public LocalDate convertFrom(LocalDate source, Type<LocalDate> destinationType) {
			return LocalDate.from(source);
		}

	}

	public static class LocalTimeConverter extends BidirectionalConverter<LocalTime, LocalTime> {

		@Override
		public LocalTime convertTo(LocalTime source, Type<LocalTime> destinationType) {
			return LocalTime.from(source);
		}

		@Override
		public LocalTime convertFrom(LocalTime source, Type<LocalTime> destinationType) {
			return LocalTime.from(source);
		}

	}


	public static class LocalDateTimeConverter extends BidirectionalConverter<LocalDateTime,
			LocalDateTime> {

		@Override
		public LocalDateTime convertTo(LocalDateTime source, Type<LocalDateTime> destinationType) {
			return LocalDateTime.from(source);
		}


		@Override
		public LocalDateTime convertFrom(LocalDateTime source,
		                                 Type<LocalDateTime> destinationType) {
			return LocalDateTime.from(source);
		}

	}


	public static class DateLocalDateTimeConverter extends BidirectionalConverter<Date,
			LocalDateTime> {

		@Override
		public LocalDateTime convertTo(Date source, Type<LocalDateTime> destinationType) {
			return LocalDateTime.ofInstant(source.toInstant(), ZoneId.systemDefault());
		}

		@Override
		public Date convertFrom(LocalDateTime source, Type<Date> destinationType) {
			return Date.from(source.atZone(ZoneId.systemDefault()).toInstant());
		}
	}


	public static class DateLocalDateConverter extends BidirectionalConverter<Date,
			LocalDate> {

		@Override
		public LocalDate convertTo(Date source, Type<LocalDate> destinationType) {
			return LocalDateTime.ofInstant(source.toInstant(), ZoneId.systemDefault())
					.toLocalDate();
		}

		@Override
		public Date convertFrom(LocalDate source, Type<Date> destinationType) {
			return Date.from(source.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
		}
	}

	public static class DateLocalTimeConverter extends BidirectionalConverter<Date,
			LocalTime> {

		@Override
		public LocalTime convertTo(Date source, Type<LocalTime> destinationType) {
			return LocalDateTime.ofInstant(source.toInstant(), ZoneId.systemDefault())
					.toLocalTime();
		}

		@Override
		public Date convertFrom(LocalTime source, Type<Date> destinationType) {
			LocalDate localDate = LocalDate.now();
			LocalDateTime localDateTime = LocalDateTime.of(localDate, source);
			return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
		}
	}

	public static class JSONConverter extends BidirectionalConverter<JSON, JSON> {

		@Override
		public JSON convertTo(JSON source, Type<JSON> destinationType) {
			return source;
		}

		@Override
		public JSON convertFrom(JSON source, Type<JSON> destinationType) {
			return source;
		}

	}

	public static class JSONObjectConverter extends BidirectionalConverter<JSONObject, JSONObject> {

		@Override
		public JSONObject convertTo(JSONObject source, Type<JSONObject> destinationType) {
			return source;
		}

		@Override
		public JSONObject convertFrom(JSONObject source, Type<JSONObject> destinationType) {
			return source;
		}

	}

	public static class JSONArrayConverter extends BidirectionalConverter<JSONArray, JSONArray> {

		@Override
		public JSONArray convertTo(JSONArray source, Type<JSONArray> destinationType) {
			return source;
		}

		@Override
		public JSONArray convertFrom(JSONArray source, Type<JSONArray> destinationType) {
			return source;
		}

	}


	private BeanMapperUtils() {
	}
}
